/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2019年1月7日
 */

package com.massyframework.beanskin.spring.beanconext.provider;


import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.web.context.support.AbstractRefreshableWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyContextDelegate;
import com.massyframework.beanskin.assembly.handling.ActivationHandler;
import com.massyframework.beanskin.assembly.handling.EnvironmentHandler;
import com.massyframework.beanskin.assembly.handling.HandlerRegistration;
import com.massyframework.beanskin.assembly.handling.ReadyingException;
import com.massyframework.beanskin.assembly.handling.ReadyingHandler;
import com.massyframework.beanskin.service.ServiceReference;
import com.massyframework.beanskin.service.ServiceRegistration;
import com.massyframework.beanskin.service.ServiceRegistry;
import com.massyframework.beanskin.service.ServiceRepository;
import com.massyframework.beanskin.spring.beancontext.DependencyServiceBeanDefinitionRegistryPostProcessor;
import com.massyframework.beanskin.spring.beancontext.DependencyServiceInitializer;
import com.massyframework.beanskin.spring.beancontext.SpringBeanContextAdapter;
import com.massyframework.beanskin.spring.beancontext.WebApplicationContextFactory;
import com.massyframework.beanskin.web.utils.ServletRegistrationUtils;

/**
 * 提供DispatcherServlet的装配件上下文处理
 */
final class SpringServletHandler extends DependencyServiceBeanDefinitionRegistryPostProcessor
        implements EnvironmentHandler, ReadyingHandler, ActivationHandler {

	 	private volatile boolean runAtJ2EE = true;
	    private volatile ServiceRegistration<Servlet> servletRegistration;
	    private volatile HandlerRegistration<AssemblyContextDelegate<SpringBeanContextAdapter>> contextRegistration;

	    private volatile ServletConfig config;
	    private volatile DispatcherServlet servlet;

	    /**
	     * 构造方法
	     */
	    public SpringServletHandler() {
	    }

	    /**
	     * 初始化
	     *
	     * @throws Exception 发生非预期的例外
	     */
	    @Override
	    protected void init() throws Exception {
	        super.init();
	        final Assembly assembly = this.getAssociatedAssembly();
	        ServiceRepository repository = this.getServiceRepository(assembly);

	        Optional<ServletContext> optional = repository.findService(ServletContext.class);
	        ServletException ex = optional.map( context -> {
	            this.runAtJ2EE = true;
	            try {
	                ServletRegistrationUtils.createServletRegistration(
	                        context,
	                        assembly,
	                        null,
	                        "true");
	            }catch(ServletException e){
	                return e;
	            }
	            return null;
	        }).orElse(null);

	        if (ex != null){
	            throw ex;
	        }
	    }

	    /* (non-Javadoc)
	     * @see com.massyframework.assembly.spi.ReadyingHandler#readying()
	     */
	    @Override
	    public synchronized void readying() throws ReadyingException {
	        if (!this.runAtJ2EE) {
	            return;
	        }

	        this.exportService();
	    }

	    /**
	     * 输出服务
	     */
	    protected synchronized void exportService() {
	        if (this.servletRegistration == null) {
	        	Assembly assembly = this.getAssociatedAssembly();
	            ServiceRepository repository = this.getServiceRepository(assembly);
	            ServiceRegistry registry = repository.getServiceRegistry();

	            Bridge servlet = new Bridge();
	            Map<String, Object> props = new HashMap<>();
	            props.put(ServiceReference.SERVICE_NAME, assembly.getConfig().getParameter("servlet.name"));

	            this.servletRegistration =
	                    registry.register(Servlet.class, servlet , props);
	        }
	    }

	    /* (non-Javadoc)
	     * @see com.massyframework.assembly.spi.ReadyingHandler#unreadyed()
	     */
	    @Override
	    public void unreadyed() throws ReadyingException {
	        this.unexportService();
	    }

	    /**
	     * 撤销服务输出
	     */
	    protected synchronized void unexportService() {
	        if (this.servletRegistration != null){
	            this.servletRegistration.unregister();
	            this.servletRegistration = null;
	        }
	    }

	    /**
	     * 在Web场景下注册的组件是否已准备就绪？<br>
	     * Servlet的实例化要视注册时的选项，如果是首次请求时才实例化，那在发起首次请求前，本方法只能返回<code>false</code>
	     *
	     * @return {@link boolean},返回<code>true</code>表示已就绪，否则返回<code>false</code>
	     */
	    @Override
	    public boolean isReady() {
	        if (!this.runAtJ2EE) {
	            return false;
	        }

	        return this.config != null;
	    }

	    /* (non-Javadoc)
	     * @see com.massyframework.assembly.spi.ActivationHandler#doStarting()
	     */
	    @Override
	    public synchronized void doStarting() throws Exception {
	        if (this.contextRegistration == null) {

	        	Assembly assembly = this.getAssociatedAssembly();
	            try {
	                WebApplicationContextFactory factory =
	                        new WebApplicationContextFactory();
	                AbstractRefreshableWebApplicationContext context =
	                        factory.createApplicationContext(assembly,
	                                new DependencyServiceInitializer<AbstractRefreshableWebApplicationContext>(this));
	                SpringBeanContextAdapter adapter =
	                        new SpringBeanContextAdapter(context);
	                AssemblyContextDelegate<SpringBeanContextAdapter> delegate =
	                		new AssemblyContextDelegate<SpringBeanContextAdapter>(adapter);
	                delegate.init(this.getLifecycleManager());
	                this.setAssemblyContext(delegate);
	                

	                this.servlet = new DispatcherServlet(context);
	                servlet.init(this.config);

	                

	                this.contextRegistration =
	                        this.getLifecycleManager()
	                            .register(delegate);
	            }catch(Exception e) {
	                throw e;
	            }
	        }
	    }

	    /* (non-Javadoc)
	     * @see com.massyframework.assembly.spi.ActivationHandler#doStopped()
	     */
	    @Override
	    public synchronized void doStopped() throws Exception {
	        if (this.contextRegistration != null) {
	            AssemblyContextDelegate<SpringBeanContextAdapter> context =
	                    this.contextRegistration.getHandler();
	            this.contextRegistration.unregister();
	            this.contextRegistration = null;

	            try {
	            	context.getBeanContext().getApplicationContext().close();
	            }finally {
		            this.servlet.destroy();
		            this.setAssemblyContext(null);
		            this.servlet = null;
	            }
	        }
	    }

	    protected void doService(ServletRequest req, ServletResponse res) throws ServletException, IOException {
	        Servlet dispatcherServlet = this.servlet;
	        if (dispatcherServlet != null) {
	            dispatcherServlet.service(req, res);
	        }
	    }

	    /**
	     * 根据<code>assembly</code>获取{@link ServiceRepository}
	     * @param assembly {@link Assembly}, 装配件
	     * @return {@link ServiceRepository}
	     */
	    protected ServiceRepository getServiceRepository(Assembly assembly){
	        return ServiceRepository.retrieveFrom(assembly);
	    }

	    /**
	     * ServletBridge在DelegateServlet和DispatcherServletEx之间的连接桥
	     */
	    private class Bridge implements Servlet {

	        @Override
	        public void init(ServletConfig config) throws ServletException {
	            SpringServletHandler.this.config = config;
	            try {
	                SpringServletHandler.this.getLifecycleManager().start();
	            } catch (Exception e) {
	                logError(e.getMessage(), e);
	                throw new ServletException(e.getMessage(), e);
	            }
	        }


	        @Override
	        public ServletConfig getServletConfig() {
	            return config;
	        }

	        @Override
	        public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
	            doService(req, res);
	        }

	        @Override
	        public String getServletInfo() {
	            return null;
	        }

	        @Override
	        public void destroy() {
	            try {
	                SpringServletHandler.this.getLifecycleManager().stop();
	            } catch (Exception e) {
	                logError(e.getMessage(), e);
	            }
	        }

	    }
}

